package com.zh.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zh.common.exception.AuthException;
import com.zh.common.exception.BusinessException;
import com.zh.common.properties.JwtProperties;
import com.zh.common.utils.RSAUtil;
import com.zh.common.utils.SHAUtil;
import com.zh.user.domain.dto.BanUserDto;
import com.zh.user.domain.dto.UpdateUserDto;
import com.zh.user.mapper.UserMapper;
import com.zh.user.service.UserService;
import com.zh.web.base.PageApiResult;
import com.zh.web.domain.auth.dto.PageUserDto;
import com.zh.web.domain.auth.dto.UserDto;
import com.zh.web.domain.auth.entity.User;
import com.zh.web.domain.auth.vo.RoleVo;
import com.zh.web.domain.auth.vo.UserVo;
import com.zh.web.enums.ProtectedRole;
import com.zh.web.enums.Status;
import com.zh.web.openApi.AuthClient;
import com.zh.web.openApi.RoleClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final JwtProperties jwtProperties;
    private final UserMapper userMapper;
    private final AuthClient authClient;
    private final RoleClient roleClient;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String updateUser(UpdateUserDto updateUserDto, HttpServletRequest request) throws Exception {
        String authToken = request.getHeader(jwtProperties.getTokenName());
        //2.是否存在这个用户
        User user = userMapper.selectById(updateUserDto.getId());
        if (ObjectUtil.isNull(user)) {
            throw new BusinessException("用户不存在");
        }
        String originalPasswordHex = user.getPassword();
        //3. 拷贝基本信息
        BeanUtil.copyProperties(updateUserDto, user);
        //4.如果有更新密码
        if (StrUtil.isNotEmpty(updateUserDto.getOldPassword()) && StrUtil.isNotEmpty(updateUserDto.getNewPassword()) && StrUtil.isNotEmpty(updateUserDto.getConfirmPassword())) {
            String salt = user.getSalt();
            String privateKey = authClient.getPrivateKey().getData();
            String oldPassword = RSAUtil.decryptByPrivateKey(updateUserDto.getOldPassword(), privateKey);
            String newPassword = RSAUtil.decryptByPrivateKey(updateUserDto.getNewPassword(), privateKey);
            String confirmPassword = RSAUtil.decryptByPrivateKey(updateUserDto.getConfirmPassword(), privateKey);
            //4.1旧密码是否正确
            String oldPasswordHex = SHAUtil.getSHA256(oldPassword, salt);
            if (!oldPasswordHex.equals(originalPasswordHex)) {
                throw new BusinessException("旧密码错误");
            }
            //4.2确认密码是否正确
            if (!newPassword.equals(confirmPassword)) {
                throw new BusinessException("两次密码不一致");
            }
            //4.3生成新的密码盐并存入
            String newSalt = SHAUtil.generateSalt();
            String newPasswordWithSalt = SHAUtil.getSHA256(newPassword, newSalt);
            user.setPassword(newPasswordWithSalt);
            user.setSalt(newSalt);
            //4.4 本人修改了密码 踢出登录
            boolean success = userMapper.updateById(user) > 0 && updateUserDto.getUpdateOperator().equals(user.getId());
            if (success) {
                authClient.logout(authToken);
            }
        }
        return userMapper.updateById(user) > 0 ? "修改成功" : "修改失败";
    }

    @Override
    public UserVo getUserInfo(UserDto userDto) {
        if ((userDto.getId() == null || userDto.getId() <= 0) && (StrUtil.isBlank(userDto.getUsername()))) {
            return null;
        }
        User user = userMapper.selectOneUser(userDto);
        if (Status.BANED.equals(user.getStatus())) {
            throw new AuthException("该账号已被封禁! 请联系管理员");
        }
        List<RoleVo> roleVoList = roleClient.getRoleByUserId(user.getId()).getData();
        UserVo userVo = BeanUtil.copyProperties(user, UserVo.class);
        userVo.setRoleList(roleVoList);
        return userVo;
    }

    @Override
    public User getUser(UserDto userDto) {
        if ((userDto.getId() == null || userDto.getId() <= 0) && (StrUtil.isBlank(userDto.getUsername()))) {
            return null;
        }
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(userDto.getId() != null && userDto.getId() > 0, User::getId, userDto.getId());
        queryWrapper.eq(StrUtil.isNotBlank(userDto.getUsername()), User::getUsername, userDto.getUsername());
        return userMapper.selectOne(queryWrapper);
    }

    @Override
    public PageApiResult<UserVo> pageUser(PageUserDto pageUserDto) {
        IPage<UserVo> page = new Page<>(pageUserDto.getPageNum(), pageUserDto.getPageSize());
        List<UserVo> userVos = userMapper.pageUser(page, pageUserDto);
        page.setRecords(userVos);
        page.getRecords().forEach(userVo -> {
            List<RoleVo> roleVoList = roleClient.getRoleByUserId(userVo.getId()).getData();
            userVo.setRoleList(roleVoList);
        });
        return PageApiResult
                .<UserVo>builder()
                .pageNum(page.getCurrent())
                .pageSize(page.getSize())
                .total(page.getTotal())
                .list(page.getRecords())
                .build();
    }

    @Override
    public UserVo getUserInfoByToken(String token) {
        //未登录获取null
        if (StrUtil.isEmpty(token)) {
            return new UserVo();
        }
        Map<String, Object> map = Optional.ofNullable(authClient.getSessionByToken(token).getData()).orElseThrow(() -> new AuthException("token无效"));
        Long userId = Long.parseLong(map.get("id").toString());
        User user = userMapper.selectById(userId);
        List<RoleVo> roleVoList = roleClient.getRoleByUserId(user.getId()).getData();
        UserVo userVo = BeanUtil.copyProperties(user, UserVo.class);
        userVo.setRoleList(roleVoList);
        return userVo;
    }

    @Override
    public String banUser(BanUserDto banUserDto) {
        //用户是否存在
        Long userId = banUserDto.getId();
        User user = Optional.ofNullable(userMapper.selectById(userId)).orElseThrow(() -> new BusinessException("用户不存在"));
        //获取用户角色 管理员不能被封禁
        List<RoleVo> roleVoList = Optional.ofNullable(roleClient.getRoleByUserId(userId).getData()).orElse(new ArrayList<>());
        boolean anyMatch = roleVoList.stream()
                .anyMatch(roleVo -> ProtectedRole.ADMIN.getRoleCode().equals(roleVo.getRoleCode()));
        if (anyMatch) {
            throw new BusinessException("不能封禁管理员");
        }
        user.setStatus(banUserDto.getStatus());
        user.setUpdateOperator(banUserDto.getUpdateOperator());
        boolean success = userMapper.updateById(user) > 0;
        if (success) {
            if (Status.ENABLE.equals(banUserDto.getStatus())) {
                return "解封成功";
            } else if (Status.BANED.equals(banUserDto.getStatus())) {
                return "封禁成功";
            }
        }
        return "操作失败";
    }

    @Override
    public List<UserVo> list(Collection<Long> userIds) {
        if (CollUtil.isEmpty(userIds)) {
            return null;
        }
        List<User> userList = userMapper.selectBatchIds(userIds);
        return userList.stream().map(user -> BeanUtil.copyProperties(user, UserVo.class)).collect(Collectors.toList());
    }

    @Override
    public Integer getUserCount() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        return userMapper.selectCount(wrapper);
    }

    @Override
    public Long addUser(User user) {
        try {
            userMapper.insert(user);
        } catch (Exception e) {
            throw new BusinessException("用户名重复~请修改");
        }
        return user.getId();
    }
}
